﻿#include "shape.h"
#include <vector>
#include <glad/glad.h>
#include <GLFW/glfw3.h>

anip::Shape::Shape(Type t) :
	type(t)
{
	glGenVertexArrays(1, &_vao) ERROR_CHECKED;
}

anip::Shape::Shape(Type t, const float* points, int n, int dimension, StorageHint storage):
	type(t)
{
	assert(n >= 0 && dimension >= 2 && dimension <= 3);

	VertexData* d = new VertexData;
	d->_size = n;
	d->_dim = dimension;
	d->_storage = storage;
	_data.push_back(d);

	glGenBuffers(1, &d->_vbo) ERROR_CHECKED;
	glBindBuffer(GL_ARRAY_BUFFER, d->_vbo) ERROR_CHECKED;
	glBufferData(GL_ARRAY_BUFFER, n * dimension * sizeof(float), 
		points, (GLenum)storage) ERROR_CHECKED;

	glGenVertexArrays(1, &_vao) ERROR_CHECKED;
	glBindVertexArray(_vao) ERROR_CHECKED;
	glEnableVertexAttribArray(0) ERROR_CHECKED;
	glVertexAttribPointer(0, dimension, GL_FLOAT, GL_FALSE, 0, nullptr) ERROR_CHECKED;
}

void anip::Shape::VertexData::set(int n, const float* data, StorageHint storage)
{
	assert(n > 0);

	_size = n;
	_storage = storage;
	glBindBuffer(GL_ARRAY_BUFFER, _vbo) ERROR_CHECKED;
	glBufferData(GL_ARRAY_BUFFER, n * _dim * sizeof(float),
		data, (GLenum)storage) ERROR_CHECKED;
}

void anip::Shape::VertexData::setPartial(int offset, int n, const float* data)
{
	assert(_size != 0);
	assert(offset >= 0 && n > 0);
	assert(offset + n <= _size);

	//if (_storage == StorageHint::Static)
	//	cerr << "Warning: setPartial called while StorageHint is static" << endl;

	glBindBuffer(GL_ARRAY_BUFFER, _vbo) ERROR_CHECKED;
	glBufferSubData(GL_ARRAY_BUFFER, offset, n, data) ERROR_CHECKED;
}

void anip::Shape::use()
{
	glBindVertexArray(_vao) ERROR_CHECKED;
}

void anip::Shape::draw()
{
	if (_data.size() == 0 || _data[0]->_size == 0) return;
	glBindVertexArray(_vao) ERROR_CHECKED;
	glDrawArrays((GLuint)type, 0, _data[0]->_size) ERROR_CHECKED;
}

anip::Shape::VertexData& anip::Shape::addDataSlot(int dimension)
{
	VertexData* d = new VertexData;
	d->_dim = dimension;
	_data.push_back(d);

	glGenBuffers(1, &d->_vbo) ERROR_CHECKED;

	return *d;
}

anip::Shape::VertexData& anip::Shape::addDataSlot(int n, int dimension, 
	const float* data, StorageHint storage)
{
	VertexData* d = new VertexData;
	d->_size = n;
	d->_dim = dimension;
	_data.push_back(d);

	glGenBuffers(1, &d->_vbo) ERROR_CHECKED;
	d->set(n, data, storage);

	return *d;
}

void anip::Shape::bindData(int iData, int iAttrib, int dimension, int stride, int offset)
{
	assert(iData >= 0 && (unsigned int)iData < _data.size());
	VertexData* d = _data[iData];
	assert(dimension == -1 || (dimension <= d->_dim && dimension > 0 && dimension <= 4));

	glBindBuffer(GL_ARRAY_BUFFER, d->_vbo) ERROR_CHECKED;
	glBindVertexArray(_vao) ERROR_CHECKED;
	glEnableVertexAttribArray(iAttrib) ERROR_CHECKED;
	glVertexAttribPointer(iAttrib, dimension == -1 ? d->_dim : dimension, GL_FLOAT, GL_FALSE, 
		stride * sizeof(float), (void*)(offset * sizeof(float))) ERROR_CHECKED;
}

void anip::Shape::finalize()
{
	for (VertexData* d : _data)
	{
		glDeleteBuffers(1, &d->_vbo) ERROR_CHECKED;
		delete d;
	}
	glDeleteVertexArrays(1, &_vao) ERROR_CHECKED;
}